package com.ghs.securityprojectboot.config;

import com.alibaba.fastjson.JSONObject;
import com.ghs.securityprojectboot.entity.DbAccount;
import com.ghs.securityprojectboot.entity.vo.RestBean;
import com.ghs.securityprojectboot.entity.vo.response.AuthorizeVO;
import com.ghs.securityprojectboot.filters.JwtAuthenticationFilter;
import com.ghs.securityprojectboot.service.IDbAccountService;
import com.ghs.securityprojectboot.util.JwtUtils;
import jakarta.annotation.Resource;
import jakarta.servlet.http.HttpServletRequest;
import jakarta.servlet.http.HttpServletResponse;
import lombok.extern.slf4j.Slf4j;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.security.access.AccessDeniedException;
import org.springframework.security.authentication.AuthenticationManager;
import org.springframework.security.authentication.ProviderManager;
import org.springframework.security.authentication.dao.DaoAuthenticationProvider;
import org.springframework.security.config.annotation.web.builders.HttpSecurity;
import org.springframework.security.config.annotation.web.configuration.EnableWebSecurity;
import org.springframework.security.config.annotation.web.configurers.AbstractHttpConfigurer;
import org.springframework.security.core.Authentication;
import org.springframework.security.core.AuthenticationException;
import org.springframework.security.core.userdetails.User;
import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder;
import org.springframework.security.crypto.password.PasswordEncoder;
import org.springframework.security.web.SecurityFilterChain;
import org.springframework.security.web.authentication.UsernamePasswordAuthenticationFilter;
import org.springframework.security.web.authentication.rememberme.JdbcTokenRepositoryImpl;
import org.springframework.security.web.authentication.rememberme.PersistentTokenRepository;
import org.springframework.web.cors.CorsConfiguration;
import org.springframework.web.cors.CorsConfigurationSource;
import org.springframework.web.cors.UrlBasedCorsConfigurationSource;

import javax.sql.DataSource;
import java.io.IOException;
import java.io.PrintWriter;

@Slf4j
@Configuration // 标识为配置类
@EnableWebSecurity // 开启 Spring Security 支持
public class SecurityConfiguration {
    @Resource // 通过依赖注入获取 DataSource
    DataSource dataSource;
    @Resource
    JwtAuthenticationFilter jwtAuthenticationFilter;
    @Resource
    IDbAccountService dbAccountService;
    @Resource
    JwtUtils jwtUtils;
    // 定义 SecurityFilterChain Bean，配置 HTTP 安全规则
    @Bean
    public SecurityFilterChain securityFilterChain(HttpSecurity http) throws Exception {
        return http
                .csrf(AbstractHttpConfigurer::disable) // 跨站请求伪造 禁用 CSRF 保护
                .authorizeHttpRequests(authorize -> authorize
                        .requestMatchers("/api/auth/**").permitAll() // 允许所有匹配 /api/auth/* 的请求
                        .anyRequest().authenticated() // 其他所有请求都需要认证
                )
                .formLogin(formLogin -> formLogin
                        .loginProcessingUrl("/api/auth/login") // 指定登录请求的处理 URL
                        .successHandler(this::onAuthenticationSuccess) // 登录成功处理
                        .failureHandler(this::onAuthenticationFailure) // 登录失败处理
                )
                .logout(logout -> logout
                        .logoutUrl("/api/auth/logout") // 指定注销的 URL
                        .logoutSuccessHandler(this::onAuthenticationSuccess) // 注销成功处理
                )
                .addFilterBefore(jwtAuthenticationFilter, UsernamePasswordAuthenticationFilter.class)//把过滤器放到UsernamePasswordAuthenticationFilter之前优先校验token是否过期
                .exceptionHandling(exceptionHandling ->exceptionHandling.accessDeniedHandler(this::accessDeniedHandler))//权限不足处理
                .cors(csrf -> csrf.configurationSource(this.corsConfigurationSource())) //跨源资源共享    配置 CORS
                .build(); // 构建并返回 HttpSecurity 配置
    }

    // 定义 AuthenticationManager Bean，用于认证管理
    @Bean
    public AuthenticationManager authenticationManager() {
        DaoAuthenticationProvider authenticationProvider = new DaoAuthenticationProvider();
        authenticationProvider.setUserDetailsService(dbAccountService); // 设置用户详情服务
        authenticationProvider.setPasswordEncoder(this.passwordEncoder()); // 设置密码编码器
        return new ProviderManager(authenticationProvider); // 返回认证管理器
    }

    // 定义 PasswordEncoder Bean，用于密码编码
    @Bean
    public PasswordEncoder passwordEncoder() {
        return new BCryptPasswordEncoder();//对密码进行加密处理
    }
    public static void main(String[] args) {
        System.out.println(new BCryptPasswordEncoder().encode("password"));
    }

    // 登录成功处理方法
    public void onAuthenticationSuccess(HttpServletRequest request, HttpServletResponse response, Authentication authentication) throws IOException {
        response.setContentType("application/json;charset=utf-8");
        PrintWriter writer = response.getWriter();
        if(request.getRequestURI().endsWith("/login"))
        {
            User user = (User) authentication.getPrincipal();
            DbAccount account = dbAccountService.findAccountByNameOrEmail(user.getUsername());
            String jwt = jwtUtils.createJwt(user, account.getUsername(), account.getId());
            AuthorizeVO vo = account.asViewObject(AuthorizeVO.class, o -> o.setToken(jwt));
            vo.setExpire(jwtUtils.expireTime());
            writer.write(RestBean.success(vo).asJsonString());
        } else if(request.getRequestURI().endsWith("/logout"))
            response.getWriter().write(JSONObject.toJSONString(RestBean.success("退出登录成功"))); // 注销成功返回成功消息
    }

    // 登录失败处理方法
    public void onAuthenticationFailure(HttpServletRequest request, HttpServletResponse response, AuthenticationException exception) throws IOException {
        response.setContentType("application/json;charset=utf-8");
        response.getWriter().write(JSONObject.toJSONString(RestBean.failure(401, exception.getMessage()))); // 登录失败返回失败消息
    }
    // 权限不足处理方法
    private void accessDeniedHandler(HttpServletRequest request, HttpServletResponse response, AccessDeniedException e) throws IOException {
        response.setContentType("application/json;charset=utf-8");
        response.getWriter().write(JSONObject.toJSONString(RestBean.failure(403, "权限不足"))); // 登录失败返回失败消息
    }
    // 配置 CORS
    private CorsConfigurationSource corsConfigurationSource() {
        CorsConfiguration cors = new CorsConfiguration(); // 创建 CORS 配置
        cors.addAllowedOriginPattern("*"); // 允许所有域的跨域请求
        cors.setAllowCredentials(true); // 允许携带凭证
        cors.addAllowedHeader("*"); // 允许所有头
        cors.addAllowedMethod("*"); // 允许所有方法
        cors.addExposedHeader("*"); // 允许所有响应头暴露
        UrlBasedCorsConfigurationSource source = new UrlBasedCorsConfigurationSource(); // 创建基于 URL 的 CORS 配置源
        source.registerCorsConfiguration("/**", cors); // 为所有路径注册 CORS 配置
        return source; // 返回 CORS 配置源
    }
}